import L from 'leaflet'
import echarts from 'echarts'

import * as charts from './charts';

const defaultOptions = {
  forcedRerender: false,
  hideOnMoving: false,
  hideOnZooming: false,
  convertTypes: ['pie', 'line', 'bar'],
}

function isObject(value) {
  let type = typeof value
  return value != null && (type === 'object' || type === 'function')
}

let id = 0

function uniqueId(prefix) {
  return prefix + id++
}

const EChartsLayer = (L.EChartsLayer = L.Layer.extend({
  initialize(chartOptions, options) {
    this.options = Object.assign({}, defaultOptions, options)
    // 图表数据
    this._chartOptions = Object.assign({}, chartOptions)

    this._map = null
    this._chart = null
    this._container = null

    this._isRegistered = false
  },

  onAdd(map) {
    this._map = map

    const container = (this._container = L.DomUtil.create(
      'div',
      'leaflet-layer leaflet-echarts-layer'
    ))
    const size = map.getSize()
    container.style.width = size.x + 'px'
    container.style.height = size.y + 'px'

    map.getPanes().overlayPane.appendChild(container)

    this._chart = echarts.init(container)

    if (this._chartOptions) {
      this.registerMap()
      this._chart.setOption(this.convertData(this._chartOptions), false)
    }

    this.bindEvent(map)
  },

  bindEvent(map) {
    map.on(this.getEvents())
  },

  unbindEvent(map) {
    map.off(this.getEvents())
  },

  getEvents() {
    return {
      resize: this.onResize.bind(this),
      zoomstart: this.onZoomStart.bind(this),
      zoomend: this.onZoomEnd.bind(this),
      movestart: this.onMoveStart.bind(this),
      moveend: this.onMoveEnd.bind(this),
    }
  },

  onResize(e) {
    this.updateViewSize(e.newSize)
    this.clearAndRedraw()
  },

  updateViewSize(size) {
    this._container.style.width = size.x + 'px'
    this._container.style.height = size.y + 'px'
  },

  clearAndRedraw() {
    if (!this._chart) return

    if (this.options.forcedRerender) {
      this._chart.clear()
    }

    this._chart.resize()
    if (this._chartOptions) {
      this._chart.setOption(this.convertData(this._chartOptions), false)
    }
  },

  onZoomStart() {
    this.options.hideOnZooming && this.innerHide()
  },

  onZoomEnd() {
    this.options.hideOnZooming && this.innerShow()
    this.clearAndRedraw()
  },

  onMoveStart() {
    this.options.hideOnMoving && this.innerHide()
  },

  onMoveEnd() {
    this.options.hideOnMoving && this.innerShow()

    const topLeft = this._map.containerPointToLayerPoint([0, 0])
    L.DomUtil.setPosition(this._container, topLeft)

    this.clearAndRedraw()
  },

  onRemove(map) {
    this._chart.dispose()

    map.getPanes().overlayPane.removeChild(this._container)

    this.unbindEvent(map)

    this._container = null
    this._chart = null
  },

  innerShow() {
    if (this._container) {
      this._container.style.display = ''
    }
  },

  innerHide() {
    if (this._container) {
      this._container.style.display = 'none'
    }
  },

  addTo(map) {
    map.addLayer(this)
    return this
  },

  registerMap() {
    if (!this._isRegistered) {
      this.coordinateSystemId = uniqueId('leaflet-echarts-')
      echarts.registerCoordinateSystem(
        this.coordinateSystemId,
        this.getCoordinateSystem(this.options)
      )
      this._isRegistered = true
    }

    if (this._chartOptions) {
      const series = this._chartOptions.series
      if (series && isObject(series)) {
        const convertTypes = this.options.convertTypes
        if (convertTypes) {
          for (let i = series.length - 1; i >= 0; i--) {
            if (!(convertTypes.indexOf(series[i].type) > -1)) {
              series[i].coordinateSystem = this.coordinateSystemId
            }
            series[i].animation = false
          }
        }
      }
    }
  },

  convertData(options) {
    const series = options.series
    if (series && series.length > 0) {
      if (!this._coordinateSystem) {
        const Rc = this.getCoordinateSystem(this.options)
        this._coordinateSystem = new Rc(this._map)
      }
      if (series && isObject(series)) {
        const convertTypes = this.options.convertTypes
        if (convertTypes) {
          for (let i = series.length - 1; i >= 0; i--) {
            if (convertTypes.indexOf(series[i].type) > -1) {
              // eslint-disable-next-line no-prototype-builtins
              if (series[i] && series[i].hasOwnProperty('coordinates')) {
                series[i] = charts[series[i].type](
                  options,
                  series[i],
                  this._coordinateSystem
                )
              }
            }
          }
        }
      }
    }
    return options
  },

  getCoordinateSystem() {
    const map = this._map
    const coordinateSystemId = this.coordinateSystemId

    const LeafletCoordSys = function (map) {
      this.map = map
      this._mapOffset = [0, 0]
      this.dimensions = ['lng', 'lat']
    }

    LeafletCoordSys.dimensions = LeafletCoordSys.prototype.dimensions || [
      'lng',
      'lat',
    ]

    LeafletCoordSys.prototype.getZoom = function () {
      return this.map.getZoom()
    }

    LeafletCoordSys.prototype.dataToPoint = function (data) {
      const point = new L.LatLng(data[1], data[0])
      const px = this.map.latLngToLayerPoint(point)

      const topLeft = this.map.containerPointToLayerPoint([0, 0])
      return [px.x - topLeft.x, px.y - topLeft.y]
    }

    LeafletCoordSys.prototype.pointToData = function (pt) {
      const topLeft = this.map.containerPointToLayerPoint([0, 0])
      const coord = this.map.layerPointToLatLng({
        x: pt[0] + topLeft.x,
        y: pt[1] + topLeft.y,
      })
      return [coord.lng, coord.lat]
    }

    LeafletCoordSys.prototype.getViewRect = function () {
      const size = this.map.getSize()
      return new echarts.graphic.BoundingRect(0, 0, size.x, size.y)
    }

    LeafletCoordSys.prototype.getRoamTransform = function () {
      return echarts.matrix.create()
    }

    LeafletCoordSys.create = function (echartsModel) {
      echartsModel.eachSeries(seriesModel => {
        if (seriesModel.get('coordinateSystem') === coordinateSystemId) {
          seriesModel.coordinateSystem = new LeafletCoordSys(map)
        }
      })
    }

    return LeafletCoordSys
  },
}))

L.eChartsLayer = function (chartOptions, options) {
  return new EChartsLayer(chartOptions, options)
}
